/*****

	IMPORTANT:
	THIS FILE IS NOT COMPLETE. IT BUILDS, BUT IS NOT FULLY FUNCTIONAL.

*****/

#include <common.h>
#include <monitor.h>
#include <drivers/ps2mouse.h>
#include <Starflub/Starflub.h>

//find wheel
unsigned char ps2_init_string[] =
	{0xf3, 0xc8, 0xf3, 0x64, 0xf3, 0x50, 0x00};

//find buttons
unsigned char ps2_init_string2[] =
	{0xf3, 0xc8, 0xf3, 0xc8, 0xf3, 0x50, 0x00};

//real init
unsigned char ps2_init_string3[] =
	{0xf6, 0xe6, 0xf4, 0xf3, 0x64, 0xe8, 0x03, 0x00};

unsigned char extwheel = 0xff, extbuttons = 0xff;

int mx = 0, my = 0, mz = 0;

unsigned int buttons[5] = {0,0,0,0,0};


void kbd_command_send(unsigned int port, unsigned char val)
{
 unsigned long timeout;
 unsigned char stat;

 for (timeout = 500000l; timeout != 0; timeout--)
 {
	stat = inb(KEYB_CTRL);
	if ((stat & 0x02) == 0) break;
 }

 if (timeout != 0) outb(port, val);
 else monitor_write("didn't send!\n");
}

unsigned char kbd_command_read()
{
 unsigned long timeout;
 unsigned char stat, data;

 for (timeout = 500000l; timeout != 0; timeout--)
 {
	stat = inb(KEYB_CTRL);
	if ((stat & 0x01) != 0)
	{
	 data = inb(KEYB_PORT);
	 if ((stat & 0xC0) == 0) return data;
	}
 }
 monitor_write("didn't read!\n");
 return -1;
}
unsigned char kbd_command_send_ack(unsigned int port, unsigned char val) {
	unsigned int ret_val=0;
	kbd_command_send(port, val);
	ret_val = kbd_command_read();
	
	if(ret_val != KEYB_ACK) {
		SFKernelPanic("ack failure", "ps2mouse");
		for(;;);
	}
	return ret_val;
}

void install_ps2_driver() {
	unsigned char *ch = 0;
	kbd_command_send(KEYB_CTRL, 0xA8);
	kbd_command_send(KEYB_CTRL, 0xA9);
	
	kbd_command_send(KEYB_CTRL, 0xD4);
	kbd_command_send_ack(KEYB_PORT, 0xF2);
	extwheel = kbd_command_read();
	
	for(ch = ps2_init_string; *ch != '\0'; ch++) {
		kbd_command_send(KEYB_CTRL, 0xD4);
		kbd_command_send_ack(KEYB_PORT, *ch);
	}
	
	kbd_command_send(KEYB_CTRL, 0xD4);
	kbd_command_send_ack(KEYB_PORT, 0xF2);
	extbuttons = kbd_command_read();
	
	kbd_command_send(KEYB_CTRL, 0xD4);
	kbd_command_send(KEYB_PORT, 0xF4);
	
//	old_handler = getvect(MOUSE_INTERRUPT_NO);
//	setvect(MOUSE_INTERRUPT_NO, new_handler);
	register_interrupt_handler(IRQ12, &new_handler);
	
	outb(0xA1, inb(0xA1)&~0x10);
}

int ok=0;
void new_handler(registers_t regs) {
	static unsigned char buf[4] = {0,0,0,0};
	int dx, dy, dz;
	
	asm volatile("cli");
	
	kbd_command_send(KEYB_CTRL, 0xAD);
	ok = 1;
	
	buf[0] = kbd_command_read();
	buf[1] = kbd_command_read();
	buf[2] = kbd_command_read();
	buf[3] = 0;
	
	if((extwheel == 0x03) || (extbuttons == 0x04))
		buf[3] = kbd_command_read();
		
	buttons[0] = buf[0] & 1;
	buttons[1] = (buf[0] & 2) >> 1;
	buttons[2] = (buf[0] & 4) >> 2;
	buttons[3] = (buf[3] & 0x10) >> 4;
	buttons[4] = (buf[3] & 0x20) >> 5;
	dx = (buf[0] & 0x10) ? buf[1]-256 : buf[1];
	dy = (buf[0] & 0x20) ? -(buf[2]-256) : -buf[2];
	dz = (buf[3] & 0x08) ? (buf[3]&7)-8 : buf[3]&7;
	
	mx+=dx;
	my+=dy;
	mz+=dz;
	
	if(mx>80)mx=80;
	if(mx<1)mx=1;
	if(my>24)mx=24;
	if(my<1)mx=1;
	
	kbd_command_send(KEYB_CTRL, 0xAE);
	
	asm volatile("sti");
	
	outb(0xA0, 0x20);
	outb(0x20, 0x20);
}

void remove_ps2_driver() {
	outb(0xA1, inb(0xA1)|0x10);
	//	setvect(MOUSE_INTERRUPT_NO, old_handler);
	unregister_interrupt_handler(IRQ12);
	
	kbd_command_send(KEYB_CTRL, 0xD4);
	kbd_command_send(KEYB_PORT, 0xF5);
	
	kbd_command_send(KEYB_CTRL, 0xD4);
	kbd_command_send(KEYB_PORT, 0xFF);
}

